/******************************************************************************* * Copyright (c) 2010, 2011 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Obeo - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.docs.intent.client.ui.editor.completion; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.util.Collection; import java.util.Iterator; import java.util.regex.Pattern; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.templates.TemplateProposal; import org.eclipse.mylyn.docs.intent.client.ui.editor.scanner.IntentPartitionScanner; import org.eclipse.mylyn.docs.intent.collab.common.query.TraceabilityInformationsQuery; import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.ReadOnlyException; import org.eclipse.mylyn.docs.intent.collab.handlers.adapters.RepositoryAdapter; import org.eclipse.mylyn.docs.intent.core.modelingunit.InstanciationInstruction; import org.eclipse.mylyn.docs.intent.parser.IntentKeyWords; /** * Computes the completion proposal for ModelingUnits. * * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> */ public class ModelingUnitCompletionProcessor extends AbstractIntentCompletionProcessor { /** * Constant for right parenthesis. */ private static final String RIGHT_PAR = ")"; /** * Constant for dots. */ private static final String DOT = "."; /** * Constant for new lines. */ private static final String NEW_LINE = "\n\t"; /** * Constant for new keyword. */ private static final String NEW_ENTITY_KEYWORD = "new"; /** * Constant for resource declaration keyword. */ private static final String RESOURCE_DECLARATION_KEYWORD = "Resource"; /** * Constant for external content references instructions. */ private static final String REF_KEYWORD = "@ref"; /** * Path of the icon for new elements. */ private static final String MODELINGUNIT_NEW_ELEMENT_ICON = "icon/outline/modelingunit_new_element.png"; /** * Path of the icon for resource declarations. */ private static final String MODELINGUNIT_RESOURCE_ICON = "icon/outline/modelingunit_resource.gif"; /** * Regular expression for identifiers. */ private static final String IDENTIFIER_REGEXP = "([a-zA-z0-9_-]+)"; /** * Constant to indentifier delimiters in qualified names. */ private static final String QUALIFIED_NAME_DELIMITER = "\\."; /** * Query used to get Traceability-related information. */ private TraceabilityInformationsQuery traceabilityInfoQuery; /** * Creates the completion processor. * * @param repositoryAdapter * the repository adapter */ public ModelingUnitCompletionProcessor(RepositoryAdapter repositoryAdapter) { super(repositoryAdapter); } /** * {@inheritDoc} * * @see org.eclipse.mylyn.docs.intent.client.ui.editor.completion.AbstractIntentCompletionProcessor#computeCompletionProposals() */ @Override protected ICompletionProposal[] computeCompletionProposals() { this.traceabilityInfoQuery = new TraceabilityInformationsQuery(repositoryAdapter); Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); try { // Step 1: get the focused instruction String text = document.get(0, offset); // get the currently considered modeling unit int startOffset = getLastIndexOf(text, Pattern.compile("@M")); if (startOffset > -1) { text = text.substring(startOffset); } String intialText = text; // remove instructions inside closed brackets text = removeInstructionsInsideClosedBrackets(text); // remove instructions that are finished (ending with ";") text = removeEndedInstructions(text); // Step 2: get the last relevant keyword String lastRelevantKeyWord = getLastRelevantKeyWord(text); // Step 3: according to this keyword, compute the proposals if (lastRelevantKeyWord == null) { proposals.addAll(getProposalsForEmptyModelingUnit("".equals(intialText), "")); } else { proposals.addAll(computeCompletionProposalsFromText(text, lastRelevantKeyWord)); } } catch (BadLocationException e) { // Nothing to do, no completion will be proposed } catch (ReadOnlyException e) { // Nothing to do, no completion will be proposed } catch (IllegalArgumentException e) { // Nothing to do, no completion will be proposed } if (proposals.isEmpty()) { proposals.add(createTemplateProposal("", "No completion available", "", "icon/outline/default.gif")); } return proposals.toArray(new ICompletionProposal[proposals.size()]); } /** * According to the given text and last relevant keyword, returns the available * {@link ICompletionProposal}s. * * @param text * the text of the current Modeling Unit * @param lastRelevantKeyWord * the last relevant keyword typed by user * @throws ReadOnlyException * the available {@link ICompletionProposal}s * @return the available {@link ICompletionProposal}s */ private Collection<ICompletionProposal> computeCompletionProposalsFromText(String text, String lastRelevantKeyWord) throws ReadOnlyException { Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); if ("".equals(lastRelevantKeyWord)) { proposals.addAll(getProposalsForEmptyModelingUnit(false, text.trim())); } else if (NEW_ENTITY_KEYWORD.equals(lastRelevantKeyWord)) { proposals.addAll(getProposalsForNewInstruction(text)); } else if (IntentKeyWords.INTENT_KEYWORD_OPEN.equals(lastRelevantKeyWord)) { proposals.addAll(getProposalsForStructuralFeatureAffectation(text)); } else if (IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL.equals(lastRelevantKeyWord) || IntentKeyWords.MODELING_UNIT_AFFECTATION_MULTI_VAL.equals(lastRelevantKeyWord)) { proposals.addAll(getProposalsForStructuralFeatureValue(text)); } return proposals; } /** * For an empty modeling unit, we provide the following {@link ICompletionProposal}s : * <ul> * <li>Resource declaration</li> * <li>new entity</li> * <li>contribute to an existing entity.</li> * </ul> * * @param isAtMUBeggining * if the cursor is at MU beginning * @param text * the text current written (e.g. 'new ') * @return the {@link ICompletionProposal}s * @throws ReadOnlyException * if errors occur while reading repository content */ private Collection<? extends ICompletionProposal> getProposalsForEmptyModelingUnit( boolean isAtMUBeggining, String text) throws ReadOnlyException { Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); String prefix = NEW_LINE; if (isAtMUBeggining) { prefix = "@M" + NEW_LINE; } // First proposal : new Resource Declaration if (text.length() == 0 || RESOURCE_DECLARATION_KEYWORD.startsWith(text)) { proposals.add(createResourceDeclarationProposal(prefix)); } // Second proposal : new entity if (text.length() == 0 || NEW_ENTITY_KEYWORD.startsWith(text)) { proposals.add(createNewEntityProposal(prefix)); } // Third proposal : new internal resource if (text.length() == 0 || REF_KEYWORD.startsWith(text)) { proposals.add(createNewInternalRefProposal(prefix)); } // Fourth proposal : contribute to an existing entity for (InstanciationInstruction instruction : traceabilityInfoQuery.getInstanciations()) { if (instruction.getName() != null && (text.length() == 0 || instruction.getName().startsWith(text))) { String description = "Contribute to the " + instruction.getName() + IntentKeyWords.INTENT_WHITESPACE; if (instruction.getMetaType() != null && instruction.getMetaType().getTypeName() != null) { description += instruction.getMetaType().getTypeName(); } else { description += "entity"; } proposals.add(createTemplateProposal(instruction.getName() + " (contribution)", description, prefix + instruction.getName() + " {\n\t\t${}\n\t}", "icon/outline/modelingunit_contribution.png")); } } return proposals; } /** * Returns all the {@link ICompletionProposal}s allowing to create new entities which name matches the * given text. * * @param text * the beginning of the entity name to create * @return all the {@link ICompletionProposal}s allowing to create new entities which name matches the * given text * @throws ReadOnlyException * if errors occur while reading repository content */ private Collection<? extends ICompletionProposal> getProposalsForNewInstruction(String text) throws ReadOnlyException { String classNameBeginning = text.substring(text.lastIndexOf(NEW_ENTITY_KEYWORD)) .replace(NEW_ENTITY_KEYWORD, "").trim(); return getProposalsForEClassifier(classNameBeginning); } /** * Returns all the {@link ICompletionProposal}s allowing to set a value to a feature starting wit the * given name. * * @param text * the beginning of the entity name to create * @return all the {@link ICompletionProposal}s allowing to set a value to a feature starting wit the * given name * @throws ReadOnlyException * if errors occur while reading repository content */ private Collection<ICompletionProposal> getProposalsForStructuralFeatureAffectation(String text) throws ReadOnlyException { Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); // Step 1: extract contribution name String contributionName = ""; String featureNameBeginning = ""; boolean isContribution = true; boolean isResourceDeclaration = false; if (text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN) != -1) { contributionName = text.substring(0, text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN)).trim(); if (contributionName.contains(IntentKeyWords.INTENT_LINEBREAK)) { String[] split = contributionName.split(IntentKeyWords.INTENT_LINEBREAK); contributionName = split[split.length - 1].trim(); } featureNameBeginning = text.substring(text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN)) .replace(IntentKeyWords.INTENT_KEYWORD_OPEN, "").trim(); if (contributionName.contains(NEW_ENTITY_KEYWORD)) { isContribution = false; contributionName = contributionName.substring( contributionName.lastIndexOf(NEW_ENTITY_KEYWORD)).trim(); contributionName = contributionName.substring( contributionName.indexOf(IntentKeyWords.INTENT_WHITESPACE)).trim(); if (contributionName.contains(IntentKeyWords.INTENT_WHITESPACE)) { contributionName = contributionName.substring(0, contributionName.indexOf(IntentKeyWords.INTENT_WHITESPACE)).trim(); } } if (contributionName.contains(RESOURCE_DECLARATION_KEYWORD)) { isResourceDeclaration = true; isContribution = false; } } // If the structural feature affectation is inside a contribution if (isContribution) { getProposalsForContribution(proposals, contributionName, featureNameBeginning); } else { if (isResourceDeclaration) { getProposalsForResourceDeclaration(proposals); } else { // If the structural feature affectation is inside an Instanciation instruction EClassifier classifierToConsider = getEClassifier(contributionName); if (classifierToConsider instanceof EClass) { for (EStructuralFeature feature : ((EClass)classifierToConsider) .getEAllStructuralFeatures()) { if (isSettableFeature(feature) && (featureNameBeginning.length() == 0 || feature.getName().startsWith( featureNameBeginning))) { proposals.add(createStructuralFeatureAffectationTemplateProposal( contributionName, feature)); } } } } } return proposals; } /** * Computes the {@link ICompletionProposal}s to provide for a Resource declaration. * * @param proposals * the proposals to fill */ private void getProposalsForResourceDeclaration(Collection<ICompletionProposal> proposals) { proposals.add(createTemplateProposal("Resource URI", "URI indicating the Resource location", "URI = \"${}\";", MODELINGUNIT_RESOURCE_ICON)); proposals.add(createTemplateProposal("Resource Content", "Add content to the Resource", "content += ${};", MODELINGUNIT_RESOURCE_ICON)); } /** * Computes the {@link ICompletionProposal}s to provide for a Contribution instruction. * * @param contributionName * the contribution name * @param featureNameBeginning * the beginning of the feature name * @param proposals * the proposals to fill * @throws ReadOnlyException * if errors occur while reading repository content */ private void getProposalsForContribution(Collection<ICompletionProposal> proposals, String contributionName, String featureNameBeginning) throws ReadOnlyException { for (InstanciationInstruction instruction : traceabilityInfoQuery.getInstanciations()) { if (contributionName.equals(instruction.getName())) { if (instruction.getMetaType() != null && instruction.getMetaType().getResolvedType() != null) { for (EStructuralFeature feature : instruction.getMetaType().getResolvedType() .getEAllStructuralFeatures()) { if (isSettableFeature(feature) && (featureNameBeginning.length() == 0 || feature.getName().startsWith( featureNameBeginning))) { proposals.add(createStructuralFeatureAffectationTemplateProposal( contributionName, feature)); } } } } } } /** * Computes the {@link ICompletionProposal}s to provide for a Structural feature value instruction. * * @param text * the text on which the proposal is called * @throws ReadOnlyException * if permission issue occur while querying document * @return the {@link ICompletionProposal}s to provide for a Structural feature value instruction */ private Collection<? extends ICompletionProposal> getProposalsForStructuralFeatureValue(String text) throws ReadOnlyException { Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); // Step 1: extract the classifier holding this feature // and the feature name boolean isContribution = true; boolean isResourceContribution = false; EClassifier classifierToConsider = null; String classifierName = null; String featureName = null; String beginning = ""; if (text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN) != -1) { classifierName = text.substring(0, text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN)).trim(); featureName = text.substring(text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN)) .replace(IntentKeyWords.INTENT_KEYWORD_OPEN, "").trim(); if (featureName.contains(IntentKeyWords.MODELING_UNIT_AFFECTATION_MULTI_VAL)) { beginning = featureName.substring( featureName.indexOf(IntentKeyWords.MODELING_UNIT_AFFECTATION_MULTI_VAL) + 2).trim(); featureName = featureName.substring(0, featureName.indexOf(IntentKeyWords.MODELING_UNIT_AFFECTATION_MULTI_VAL)).trim(); } else if (featureName.contains(IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL)) { beginning = featureName.substring( featureName.indexOf(IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL) + 1).trim(); featureName = featureName.substring(0, featureName.indexOf(IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL)).trim(); } if (classifierName.contains(NEW_ENTITY_KEYWORD)) { isContribution = false; classifierName = classifierName.substring(classifierName.lastIndexOf(NEW_ENTITY_KEYWORD)) .trim(); classifierName = classifierName.substring( classifierName.indexOf(IntentKeyWords.INTENT_WHITESPACE)).trim(); if (classifierName.contains(IntentKeyWords.INTENT_WHITESPACE)) { classifierName = classifierName.substring(0, classifierName.indexOf(IntentKeyWords.INTENT_WHITESPACE)).trim(); } } if (classifierName.contains(RESOURCE_DECLARATION_KEYWORD)) { isContribution = false; isResourceContribution = true; featureName = "Resource Content"; } } if (isContribution) { for (InstanciationInstruction instruction : traceabilityInfoQuery.getInstanciations()) { if (classifierName.equals(instruction.getName())) { if (instruction.getMetaType() != null && instruction.getMetaType().getResolvedType() != null) { classifierToConsider = instruction.getMetaType().getResolvedType(); break; } } } } else { if (!isResourceContribution) { classifierToConsider = getEClassifier(classifierName); } } // Step 2: get the feature type if (isResourceContribution || classifierToConsider instanceof EClass) { EStructuralFeature featureToConsider = null; if (!isResourceContribution) { featureToConsider = ((EClass)classifierToConsider).getEStructuralFeature(featureName); } if (isResourceContribution || (featureToConsider != null && featureToConsider.getEType() != null && featureToConsider .getEType().getName() != null)) { proposals.addAll(doGetProposalsForStructuralFeatureValue(isResourceContribution, featureName, beginning, featureToConsider)); } } return proposals; } /** * Computes the {@link ICompletionProposal}s to provide for a Structural Feature value instruction. * * @param isResourceContribution * indicates if we are in the scope of a resource contribution or not * @param featureName * the feature name * @param beginning * the beginning of the instruction on which completion is called * @param featureToConsider * the {@link EStructuralFeature} to consider * @throws ReadOnlyException * if permission issue occur while querying document * @return the {@link ICompletionProposal}s to provide for a Structural feature value instruction */ private Collection<ICompletionProposal> doGetProposalsForStructuralFeatureValue( boolean isResourceContribution, String featureName, String beginning, EStructuralFeature featureToConsider) throws ReadOnlyException { Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); // Step 3: if the feature is an EAttribute, we add a proposal with the default value of this // attribute type if (featureToConsider instanceof EAttribute) { String defaultAttributeValue = ""; if (featureToConsider.getEType().getDefaultValue() != null) { defaultAttributeValue = "Default: " + featureToConsider.getDefaultValue().toString(); } if (featureToConsider.getEType() instanceof EEnum) { for (EEnumLiteral literal : ((EEnum)featureToConsider.getEType()).getELiterals()) { proposals.add(createTemplateProposal("'" + literal.getName() + "' value (of type " + featureToConsider.getEType().getName() + RIGHT_PAR, defaultAttributeValue + " - Set a simple value of type " + featureToConsider.getEType().getName(), '"' + literal.getName() + "\";", "icon/outline/modelingunit_value.gif")); } } else { proposals.add(createTemplateProposal("value (of type " + featureToConsider.getEType().getName() + RIGHT_PAR, defaultAttributeValue + " - Set a simple value of type " + featureToConsider.getEType().getName(), '"' + defaultAttributeValue + "\";", "icon/outline/modelingunit_value.gif")); } } else { // Propose to create a new Element of the feature type if (!isResourceContribution) { proposals.add(createTemplateProposal("new Element (of type " + featureToConsider.getEType().getName() + RIGHT_PAR, "Set this new Element as value for " + featureToConsider.getName(), "new " + getQualifiedName(featureToConsider.getEType().getEPackage()) + DOT + featureToConsider.getEType().getName() + "{\n\t${}\n};", MODELINGUNIT_NEW_ELEMENT_ICON)); } // Propose to reference an already defined element for (InstanciationInstruction instruction : traceabilityInfoQuery.getInstanciations()) { // Instruction's name should match the beginning boolean isMatchingInstruction = false; boolean hasMatchingName = instruction.getName() != null && (beginning.length() == 0 || instruction.getName().startsWith(beginning)); if (hasMatchingName && !isResourceContribution) { // Instruction's type should match the featureToConsider type isMatchingInstruction = instruction.getMetaType() != null && (featureToConsider.getEType().equals( instruction.getMetaType().getResolvedType()) || featureToConsider .getEType() instanceof EClass && ((EClass)featureToConsider.getEType()).isSuperTypeOf(instruction .getMetaType().getResolvedType())); } else if (hasMatchingName) { // Resource content has not type so all instructions should be displayed isMatchingInstruction = true; } if (isMatchingInstruction) { proposals.add(createTemplateProposal("Reference to " + instruction.getName(), "Set the " + instruction.getName() + " element as value for " + featureName, instruction.getName(), "icon/outline/modelingunit_ref.png")); } } // If the expected eType is an EClassifier, also propose all available classifiers if (!isResourceContribution && featureToConsider.getEType().equals(EcorePackage.eINSTANCE.getEClassifier())) { proposals.addAll(getProposalsForEClassifier(beginning)); } } return proposals; } /** * Computes the {@link ICompletionProposal}s to provide when called on an EClassifier (e.g. a 'new XXX' * instruction). * * @param classNameBeginning * the beginning of the class name * @throws ReadOnlyException * if permission issue occur while querying document * @return the {@link ICompletionProposal}s to provide when called on an EClassifier */ private Collection<? extends ICompletionProposal> getProposalsForEClassifier(String classNameBeginning) throws ReadOnlyException { boolean isPrefixedByPackageName = classNameBeginning.indexOf('.') != -1; String packageNamePrefix = null; String classNameBeginningWithoutPackageDeclaration = classNameBeginning; if (isPrefixedByPackageName) { String[] split = classNameBeginning.split("\\."); packageNamePrefix = split[0]; if (split.length == 1) { classNameBeginningWithoutPackageDeclaration = ""; } else { classNameBeginningWithoutPackageDeclaration = split[1]; } } Collection<ICompletionProposal> proposals = Sets.newLinkedHashSet(); Iterator<EPackage> availablePackages = Iterables.filter( traceabilityInfoQuery.getOrCreateTraceabilityIndex().eResource().getResourceSet() .getPackageRegistry().values(), EPackage.class).iterator(); int i = 0; while (availablePackages.hasNext() && i < 100) { EPackage availablePackage = availablePackages.next(); if (!isPrefixedByPackageName || isPrefixedByPackageName && getQualifiedName(availablePackage).equals(packageNamePrefix)) { for (EClassifier availableClass : availablePackage.getEClassifiers()) { if (availableClass.getName() != null && (classNameBeginningWithoutPackageDeclaration.length() == 0 || availableClass .getName().startsWith(classNameBeginningWithoutPackageDeclaration))) { String proposalContent = availableClass.getName(); if (!isPrefixedByPackageName) { proposalContent = getQualifiedName(availablePackage) + DOT + proposalContent; } proposals.add(createTemplateProposal(availableClass.getName(), availablePackage.getNsURI(), proposalContent, MODELINGUNIT_NEW_ELEMENT_ICON)); i++; } } } } return proposals; } /** * Returns the last relevant keyword of the given text. For example : * <ul> * <li>"new Somethin" will return "new"</li> * <li>"new Something {" will return IntentKeyWords.INTENT_KEYWORD_OPEN</li> * <li>"new Something { attrib" will return IntentKeyWords.INTENT_KEYWORD_OPEN</li> * <li>"new Something { attribute =" will return IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL</li> * <li>"new Something { attribute = 'value'; will return IntentKeyWords.INTENT_KEYWORD_OPEN</li> * </ul> * * @param text * the text to analyse * @return the last relevant keyword of the given text */ private String getLastRelevantKeyWord(String text) { String lastRelevantKeyword = null; int lastNew = getLastIndexOf(text, Pattern.compile(NEW_ENTITY_KEYWORD)); int lastOpeningBracket = text.lastIndexOf(IntentKeyWords.INTENT_KEYWORD_OPEN); int lastStructuralFeatureAffectation = text .lastIndexOf(IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL); int lastMultiValuedStructuralFeatureAffectation = text .lastIndexOf(IntentKeyWords.MODELING_UNIT_AFFECTATION_MULTI_VAL); int lastKWIndex = Math.max( Math.max(lastStructuralFeatureAffectation, lastStructuralFeatureAffectation), Math.max(lastNew, lastOpeningBracket)); if (lastKWIndex != -1) { if (lastKWIndex == lastNew) { lastRelevantKeyword = NEW_ENTITY_KEYWORD; } else if (lastKWIndex == lastOpeningBracket) { lastRelevantKeyword = IntentKeyWords.INTENT_KEYWORD_OPEN; } else if (lastKWIndex == lastStructuralFeatureAffectation) { lastRelevantKeyword = IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL; } else if (lastKWIndex == lastMultiValuedStructuralFeatureAffectation) { lastRelevantKeyword = IntentKeyWords.MODELING_UNIT_AFFECTATION_MULTI_VAL; } } return lastRelevantKeyword; } /** * Removes all lines that contains instructions inside closed bracket (e.g. * "myFeature = new MyType { myOtherFeature='42';};"). * * @param text * the text to modify * @return the text without any instruction inside closed brackets */ private String removeInstructionsInsideClosedBrackets(String text) { String textWithClosedInstructionsRemoved = text; try { int nextOffsetForEndingInstruction = textWithClosedInstructionsRemoved.indexOf("}"); int nextOffsetForEndingInstruction2 = textWithClosedInstructionsRemoved.indexOf("};"); while (Math.max(nextOffsetForEndingInstruction, nextOffsetForEndingInstruction2) != -1) { String textToRemove = ""; if (nextOffsetForEndingInstruction > nextOffsetForEndingInstruction2) { textToRemove = textWithClosedInstructionsRemoved.substring(0, nextOffsetForEndingInstruction + 1); } else { textToRemove = textWithClosedInstructionsRemoved.substring(0, nextOffsetForEndingInstruction + 2); } textToRemove = textToRemove.substring(textToRemove .substring(0, textToRemove.lastIndexOf("{")).lastIndexOf( IntentKeyWords.INTENT_LINEBREAK)); textWithClosedInstructionsRemoved = textWithClosedInstructionsRemoved.replace(textToRemove, ""); nextOffsetForEndingInstruction = textWithClosedInstructionsRemoved.indexOf("}"); nextOffsetForEndingInstruction2 = textWithClosedInstructionsRemoved.indexOf("};"); } } catch (ArrayIndexOutOfBoundsException e) { // Nothing to do } catch (StringIndexOutOfBoundsException e) { // Nothing to do } return textWithClosedInstructionsRemoved; } /** * Removes all lines that contains ended instruction (e.g. "myFeature = 'myValue';"). * * @param text * the text to modify * @return the text without any ended instruction */ private String removeEndedInstructions(String text) { Document tempDoc = new Document(text); try { while (tempDoc.get().lastIndexOf(";") > -1) { int lineToRemove = tempDoc.getLineOfOffset(tempDoc.get().lastIndexOf(";")); int lineToRemoveOffset = tempDoc.getLineOffset(lineToRemove); int lineToRemoveLength = tempDoc.getLineLength(lineToRemove); String newDocContent = tempDoc.get().substring(0, lineToRemoveOffset) + tempDoc.get().substring(lineToRemoveOffset + lineToRemoveLength); tempDoc.set(newDocContent); } } catch (BadLocationException e) { // Nothing to do } return tempDoc.get(); } /** * Computes the {@link TemplateProposal} for a structural feature affection. * * @param contributionName * the contribution name * @param feature * the feature related to the structural feature affectation * @return the {@link TemplateProposal} for a structural feature affection */ private TemplateProposal createStructuralFeatureAffectationTemplateProposal(String contributionName, EStructuralFeature feature) { String affect = IntentKeyWords.MODELING_UNIT_AFFECTATION_SINGLE_VAL; if (feature.isMany()) { affect = "+" + affect; } String label = feature.getName(); if (feature.getEType() != null && feature.getEType().getName() != null) { label += " : " + feature.getEType().getName(); if (feature.getLowerBound() != 1 || feature.getUpperBound() != 1) { label += " ["; if (feature.getLowerBound() == 0 && feature.getUpperBound() == 1) { label += "?"; } else if (feature.getUpperBound() != -1) { label += feature.getLowerBound() + "," + feature.getUpperBound(); } else { label += feature.getLowerBound() + ",*"; } label += "]"; } } String description = "Set the value " + contributionName + DOT + feature.getName(); if (feature.getDefaultValue() != null) { description = "Default: " + feature.getDefaultValue() + " - " + description; } return createTemplateProposal(label, description, feature.getName() + IntentKeyWords.INTENT_WHITESPACE + affect + IntentKeyWords.INTENT_WHITESPACE, "icon/outline/modelingunit_affect.png"); } /** * Returns the {@link EClassifier} related to a Contribution with the given name. * * @param contributionName * the contribution name * @return the {@link EClassifier} related to a Contribution with the given name * @throws ReadOnlyException * if permission issue occur while querying document */ private EClassifier getEClassifier(String contributionName) throws ReadOnlyException { EClassifier classifierToConsider = null; Iterator<EPackage> availablePackages = Iterables.filter( traceabilityInfoQuery.getOrCreateTraceabilityIndex().eResource().getResourceSet() .getPackageRegistry().values(), EPackage.class).iterator(); String packageName = null; String classifierName = contributionName; if (contributionName.matches(IDENTIFIER_REGEXP + QUALIFIED_NAME_DELIMITER + IDENTIFIER_REGEXP)) { String[] split = contributionName.split(QUALIFIED_NAME_DELIMITER); packageName = split[0]; classifierName = split[1]; } while (availablePackages.hasNext() && classifierToConsider == null) { EPackage availablePackage = availablePackages.next(); if (packageName == null || packageName.equals(availablePackage.getName())) { classifierToConsider = availablePackage.getEClassifier(classifierName); } } return classifierToConsider; } /** * Indicates if the given feature can be modified and hence should be displayed in the proposal. * * @param feature * the feature to consider * @return true if the given feature can be modified and hence should be displayed in the proposal, false * otherwise */ private boolean isSettableFeature(EStructuralFeature feature) { return feature.isChangeable() && !feature.isDerived(); } /** * Returns the qualified name of the given ePackage. * * @param ePackage * the ePackage * @return the qualified name of the given ePackage */ private static String getQualifiedName(EPackage ePackage) { String res = ePackage.getName(); EPackage tmp = (EPackage)ePackage.eContainer(); while (tmp != null) { res = tmp.getName() + '.' + res; tmp = (EPackage)tmp.eContainer(); } return res; } /** * Creates a {@link ICompletionProposal} allowing to create a new entity. * * @param prefix * the prefix to use * @return a {@link ICompletionProposal} allowing to create a new entity. */ private ICompletionProposal createNewEntityProposal(String prefix) { return createTemplateProposal(NEW_ENTITY_KEYWORD, "Declaration of a new entity", prefix + "new ${Type} {}", MODELINGUNIT_NEW_ELEMENT_ICON); } /** * Creates a {@link ICompletionProposal} allowing to create a new resource declaration. * * @param prefix * the prefix to use * @return a {@link ICompletionProposal} allowing to create a new resource declaration. */ private ICompletionProposal createResourceDeclarationProposal(String prefix) { return createTemplateProposal(RESOURCE_DECLARATION_KEYWORD, "Declaration of a new Resource", prefix + "Resource myResource {\n\t\tURI = \"${}\";\n\t}", MODELINGUNIT_RESOURCE_ICON); } /** * Creates a {@link ICompletionProposal} allowing to create a new internal ref. * * @param prefix * the prefix to use * @return a {@link ICompletionProposal} allowing to create a new internal ref. */ private ICompletionProposal createNewInternalRefProposal(String prefix) { return createTemplateProposal(REF_KEYWORD, "Declaration of a new internal entity (stored only inside the intent repository)", prefix + REF_KEYWORD + " \"intent:/" + repositoryAdapter.getRepository().getIdentifier() + "/${newElementPath}\"", MODELINGUNIT_NEW_ELEMENT_ICON); } /** * {@inheritDoc} * * @see org.eclipse.mylyn.docs.intent.client.ui.editor.completion.AbstractIntentCompletionProcessor#getContextType() */ @Override public String getContextType() { return IntentPartitionScanner.INTENT_MODELINGUNIT; } }